use crate::catcher::Catcher;
use crate::com::Com;
use crate::downloader::DownloadStatus;
use crate::downloader::Downloader;
use crate::myerror::MyErrs;
use crate::url::Url;
use reqwest::blocking::get;
use std::process::exit;
use std::sync::mpsc;
use std::thread::sleep;
use std::time::Duration;

use super::ThreadsStatus;
const MAXTHEARD: u64 = 100;
impl Catcher {
    pub fn new(uid: usize) -> Self {
        let channel = mpsc::channel();
        Self {
            uid,
            download_urls: Vec::new(),
            threads_channel: channel.1,
            sender: channel.0,
            thread_status: ThreadsStatus(0, 0, 0),
            max_thread: MAXTHEARD
        }
    }
    fn get_img_url(html: String) -> Vec<Url> {
        let html = html.as_bytes();
        let mut index = 0;
        let len = html.len() - 15;
        let mut result = Vec::new();
        while index <= len {
            if String::from_utf8_lossy(&html[index..index + 14]) == "img data-src=\"" {
                index += 14;
                let mut tmp = String::new();
                while html[index] as char != '"' {
                    tmp.push(html[index] as char);
                    index += 1;
                }
                let tmp = Url::new(tmp);
                result.push(tmp);
            }
            index += 1;
        }
        result
    }
    fn get_html_by_id(id: u64) -> Result<String, MyErrs> {
        let resp = get(format!("https://www.bilibili.com/read/cv{}", id))?;
        let text = resp.text()?;
        Ok(text)
    }
}
impl Catcher {
    fn get_articles_json(&self) -> std::result::Result<String, MyErrs> {
        let spon = get(format!(
            "https://api.bilibili.com/x/space/article?mid={}",
            &self.uid
        ))?;
        Ok(spon.text()?)
    }
    pub fn get_article_ids(&self) -> Result<Vec<u64>, MyErrs> {
        let mut result = Vec::new();
        let json: serde_json::Value = serde_json::from_str(&self.get_articles_json()?)?;
        let artls = match json["data"]["articles"].as_array() {
            Some(a) => a,
            None => return Err(MyErrs::UnkownJsonParse),
        };
        let mut count = 1;
        for i in artls {
            println!("ARTICLES: {}", count);
            result.push(match i["id"].as_u64() {
                Some(a) => a,
                None => return Err(MyErrs::UnkownJsonParse),
            });
            count += 1;
        }

        Ok(result)
    }
    fn get_special_cloumn_json(&self) -> Result<String, reqwest::Error> {
        let respon = get(format!(
            "https://api.bilibili.com/x/article/up/lists?mid={}",
            self.uid
        ))?;

        respon.text()
    }
    //NOT ARTICLE ID!!!!
    //IT'S SPECIAL CLOUMN ID!!!
    fn get_special_cloumn_ids(&self) -> Result<Vec<u64>, MyErrs> {
        let mut result = Vec::new();
        let json: serde_json::Value =
            serde_json::from_str::<serde_json::Value>(&self.get_special_cloumn_json()?)?;
        let ids = match json["data"]["lists"].as_array() {
            Some(a) => a,
            None => return Err(MyErrs::UnkownJsonParse),
        };
        let mut count = 1;
        for i in ids {
            println!("SPECIAL CLOUMN:{}", count);
            result.push(i["id"].as_u64().unwrap());
            count += 1;
        }
        Ok(result)
    }
    fn get_one_special_cloumn_json(spcd_id: &u64) -> Result<String, reqwest::Error> {
        let spon = get(format!(
            "https://api.bilibili.com/x/article/list/web/articles?id={}",
            spcd_id
        ))?;
        spon.text()
    }
    //many articles ids
    pub fn get_ar_ids_by_one_sp(spcd_id: &u64) -> Result<Vec<u64>, MyErrs> {
        let mut result = Vec::new();
        let json: serde_json::Value =
            serde_json::from_str(&Self::get_one_special_cloumn_json(spcd_id)?)?;
        let ids = match json["data"]["articles"].as_array() {
            Some(a) => a,
            None => return Err(MyErrs::UnkownJsonParse),
        };
        let mut count = 1;
        for i in ids {
            println!("ARTICLES IN SPECIAL CLOUMN:{}", count);
            result.push(i["id"].as_u64().unwrap());
            count += 1;
        }
        Ok(result)
    }
    fn get_articles_ids_in_spcl(&self) -> Result<Vec<u64>, MyErrs> {
        let mut result = Vec::new();
        let spcl_ids = self.get_special_cloumn_ids()?;
        for i in spcl_ids {
            let mut tmp = Self::get_ar_ids_by_one_sp(&i)?;
            result.append(&mut tmp);
        }
        Ok(result)
    }
    pub fn push_to_download(&mut self, articles_ids: Vec<u64>) -> Result<(), MyErrs> {
        for i in articles_ids {
            println!("I'm parsing: {}", &i);
            let html = Self::get_html_by_id(i)?;
            self.download_urls.append(&mut Self::get_img_url(html));
            println!("Finished");
        }
        Ok(())
    }
    fn update_threads_status(&mut self) -> Result<(), MyErrs> {
        let mut finished_counts = 0;
        let mut succe_counts = 0;
        let mut thread_errs = Vec::new();
        for i in self.threads_channel.try_iter() {
            finished_counts += 1;
            match i {
                DownloadStatus::Success => succe_counts += 1,
                DownloadStatus::Fail(a) => thread_errs.push(a),
            }
        }
        let status = ThreadsStatus(
            self.thread_status.0,
            finished_counts + self.thread_status.1,
            succe_counts + self.thread_status.2,
        );
        self.thread_status = status;
        if thread_errs.is_empty() {
            Ok(())
        } else {
            Err(MyErrs::ThreadErrs(thread_errs))
        }
    }
}
impl Catcher {
    pub fn run_com(&mut self, com: Com) -> Result<(), MyErrs> {
        match com {
            Com::NULL => Ok(()),
            Com::SetUID(a) => {
                self.uid = a;
                Ok(())
            }
            Com::InOneColumn(b) => {
                let ids = Self::get_ar_ids_by_one_sp(&b)?;
                self.push_to_download(ids)?;
                Ok(())
            }
            Com::DefaultArticles => {
                let ids = self.get_article_ids()?;
                self.push_to_download(ids)?;
                Ok(())
            }
            Com::SpecialColumns => {
                let ids = self.get_articles_ids_in_spcl()?;
                self.push_to_download(ids)?;
                Ok(())
            }
            Com::AllArcticles => {
                self.run_com(Com::DefaultArticles)?;
                self.run_com(Com::SpecialColumns)?;
                Ok(())
            }
            Com::Start => {
                self.start_download()?;
                Ok(())
            }
            Com::Clean => {
                self.download_urls.clear();
                //self.threads.clear();
                Ok(())
            }
            Com::Health => {
                self.update_threads_status()?;
                self.thread_status.print_status();
                Ok(())
            }
            Com::Exit => {
                self.update_threads_status()?;
                let allfinished = self.thread_status.is_all_finished();
                if allfinished {
                    println!("all threads had finished\nExiting...");
                    exit(0);
                }
                println!("You can't do that!\nThere has threads not finish!");
                return Err(MyErrs::ThreadsNotFinish);
            }
        }
    }
    pub fn start_download(&mut self) -> Result<(), MyErrs> {
        let mut counts = 0;
        let mut errors_buffer = Vec::new();
        while !self.download_urls.is_empty() {
            self.thread_status.0 += 1;
            let i = self.download_urls.pop().unwrap();
            if counts >= self.max_thread {
                println!("threads too much!\nWaiting...");
                //wait
                loop {
                    sleep(Duration::from_secs_f64((self.max_thread / 2) as f64));
                    let update_results = self.update_threads_status();
                    match update_results {
                        Ok(()) => {}
                        Err(errs) => {
                            println!("There are {} errors during downloading!", errs.count());
                            errs.push_to_vec(&mut errors_buffer);
                        }
                    }
                    let downloading = self.thread_status.0 - self.thread_status.1;
                    println!("now downloading: {} threads", downloading);
                    if downloading < self.max_thread / 3 * 2 {
                        counts = downloading;
                        break;
                    }
                }
            }
            println!("URL:{}", &i);
            let downloader = Downloader::new(i.get_url_name(), i, &self.sender);
            downloader.start();
            counts += 1;
        }
        if errors_buffer.is_empty() {
            Ok(())
        } else {
            Err(MyErrs::ThreadErrs(errors_buffer))
        }
    }
}
